package com.liy.redisson.controller;

import org.redisson.api.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author ly
 * @date 2022/11/8 14:05
 *
 * RedisSon锁
 */


@RestController
public class RedisSonController {


    /**
     * 公平锁 :
     *          优先分配给先发出请求的线程。所有请求线程会在一个队列中排队，当某个线程出现宕机时，
     *          Redisson会等待5秒后继续下一个线程，也就是说如果前面有5个线程都处于等待状态，那么后面的线程会等待至少25秒。
     *
     * 联锁  : 可以将多个RLock对象关联为一个联锁，每个RLock对象实例可以来自于不同的Redisson实例。 所有的锁都上锁成功才算成功。
     *
     * 红锁  : 与联锁不同的是，大部分节点上锁成功就算成功
     *
     */

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 重入锁
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/lock")
    public Object lock(String a) throws InterruptedException {
        //对用户加锁
        RLock rLock = redissonClient.getLock("ss");

       try {
           //lock 拿不到锁会一致等待, 阻塞式等待
           // tryLock获取锁失败立即放回, 非阻塞等待
           //尝试加锁， 最多等待1秒 ，60秒后自动解除  无手动解锁   tryLock同步锁
           //如果指定了等待时间就不会看门狗就不会自动续期了
           boolean getUserLock = rLock.tryLock(1,60,TimeUnit.SECONDS);

           if (getUserLock){

                //业务
               for (int i = 0; i < 10; i++) {
                   System.out.println(i);
                   Thread.sleep(1000);
               }

                return "加锁成功";
           }
           return "获取失败";
       }catch (Exception e){
           //抛出异常
           System.out.println("接令频繁 anyLock");
       }finally {

           //解锁
           if (rLock.isLocked()){
               if (rLock.isHeldByCurrentThread()){
                   rLock.unlock();
               }
           }
       }
        return null;
    }

    /**
     * 读写锁
     *
     * * 保证一定能读到最新数据，修改期间，写锁是一个排它锁（互斥锁、独享锁），读锁是一个共享锁
     * * 写锁没释放读锁必须等待
     * * 读 + 读 ：相当于无锁，并发读，只会在Redis中记录好，所有当前的读锁。他们都会同时加锁成功
     * * 写 + 读 ：必须等待写锁释放
     * * 写 + 写 ：阻塞方式
     * * 读 + 写 ：有读锁。写也需要等待
     * * 只要有读或者写的存都必须等待
     */

    /**
     * 写锁
     */
    @GetMapping("/write")
    public String write(){
        String s = "";

        RReadWriteLock readWriteLock =   redissonClient.getReadWriteLock("lock");
        //加写锁
        RLock rLock = readWriteLock.writeLock();

        try {
            rLock.lock();
            s = UUID.randomUUID().toString();

            for (int i = 0; i < 5; i++) {
                System.out.println("更改数据中。。。。。");
                Thread.sleep(1000);
            }

            ValueOperations valueOperations = redisTemplate.opsForValue();
            valueOperations.set("writeValue",s);

            return s;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("数据更改完毕。。。。。");
            rLock.unlock();
        }
        return null;
    }

    /**
     * 读锁
     * @return
     */
    @GetMapping("/read")
    public String read(){
        String s = "";

        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("lock");
        //加读锁
        RLock rLock = readWriteLock.readLock();

        try {
            rLock.lock();

            for (int i = 0; i < 5; i++) {
                System.out.println("获取数据中。。。。。");
                Thread.sleep(1000);
            }

            ValueOperations valueOperations = redisTemplate.opsForValue();
            Object writeValue = valueOperations.get("writeValue");

            return writeValue.toString();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("获取数据完毕。。。。。。");
            rLock.unlock();
        }
        return null;
    }

    /**
     * 信号量
     *  车库停车
     *      3号位
     *   信号量也可以做分布式限流
     *
     * park.acquire() 阻塞
     * park.tryAcquire() 拿不到信号立即放回false 非阻塞
     *
     *   说明：信号量为存储在redis中的一个数字，当这个数字大于0时，即可以调用acquire()方法增加数量，
     *   也可以调用release()方法减少数量，但是当调用release()之后小于0的话方法就会阻塞，直到数字大于0。
     */

    /**
     * 获取一个信号, 获取一个值, 占一个车位
     */
    @GetMapping("/park")
    public String park() throws InterruptedException {
        //获取park对象
        RSemaphore park = redissonClient.getSemaphore("park");

       //获取型号占位置
       boolean b = park.tryAcquire();
        System.out.println(b);
       if (b){
           //业务

       }else {
           //进行限流
           return "3号位已被占领";
       }
       return "ok => 3号位已占领";
    }

    /**
     * 释放一个车位
     * @return
     */
    @GetMapping("/go")
    public String go(){

        //获取车位对象
        RSemaphore park = redissonClient.getSemaphore("park");
        //释放车位
        park.release();
        return "ok => 3号位释放成功";
    }


    /**
     * 闭锁
     *     放假 锁门
     *     1班没人了
     *     5个班全部走完了，才锁门
     *
     *  在要完成某些运算时，只有其它线程的运算全部运行完毕，当前运算才继续下去。
     *  countDown（）被调用5次后，await才会不阻塞，会继续往下走。
     */

    /**
     * 设数
     */
    @GetMapping("lockdoor")
    public String lockDoor() throws InterruptedException {

        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        boolean b = door.trySetCount(5);//设计计数为5个
        door.await();  //等待闭锁完成  否则请求一直响应
        return "放假了。。。。";
    }

    /**
     * 计数 -1
     */
    @GetMapping("gogogo")
    public String goGoGo(){
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        door.countDown();  //计数-1
        return "某某班的人都走了";
    }

    /**
     * 同步锁synchronized
     */
    @GetMapping("/sync")
    public String sync(String a){
        System.out.println(a);

//        synchronized (RestController.class){
            for (int i = 0; i < 5; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
//        }

        return "成功";
    }

}
